home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / xbmc-9.11.exe / plugins / Programs / SVN Repo Installer / installerAPI / xbmcplugin_list.py < prev    next >
Encoding:
Python Source  |  2009-11-03  |  13.2 KB  |  338 lines

  1. """
  2. svn repo installer plugin
  3.  
  4. Nuka1195
  5. """
  6.  
  7. # main imports
  8. import sys
  9. import os
  10. import xbmc
  11. import xbmcgui
  12. import xbmcplugin
  13. import urllib
  14. import re
  15. from xml.sax.saxutils import unescape
  16. #from pprint import pprint
  17.  
  18. from xbmcplugin_lib import *
  19.  
  20. # Script constants
  21. __date__ = '24-06-2009'
  22. log("Module: %s Dated: %s loaded!" % (__name__, __date__))
  23.  
  24. class Parser:
  25.     """ Parser Class: grabs all tag versions and urls """
  26.     # regexpressions
  27.     revision_regex = re.compile( '<h2>.+?Revision ([0-9]*): ([^<]*)</h2>' )
  28.     asset_regex = re.compile( '<li><a href="([^"]*)">([^"]*)</a></li>' )
  29.  
  30.     def __init__( self, htmlSource ):
  31.         log("%s __init__!" % (self.__class__))
  32.         # set our initial status
  33.         self.dict = { "status": "fail", "revision": 0, "assets": [], "url": "" }
  34.         # fetch revision number
  35.         self._fetch_revision( htmlSource )
  36.         # if we were successful, fetch assets
  37.         if ( self.dict[ "revision" ] != 0 ):
  38.             self._fetch_assets( htmlSource )
  39.  
  40.     def _fetch_revision( self, htmlSource ):
  41.         try:
  42.             # parse revision and current dir level
  43.             revision, url = self.revision_regex.findall( htmlSource )[ 0 ]
  44.             # we succeeded :), set our info
  45.             self.dict[ "url" ] = url
  46.             self.dict[ "revision" ] = int( revision )
  47.         except:
  48.             pass
  49.  
  50.     def _fetch_assets( self, htmlSource ):
  51.         try:
  52.             assets = self.asset_regex.findall( htmlSource )
  53.             if ( len( assets ) ):
  54.                 for asset in assets:
  55.                     if ( asset[ 0 ] != "../" ):
  56.                         self.dict[ "assets" ] += [ unescape( asset[ 0 ] ) ]
  57.                 self.dict[ "status" ] = "ok"
  58.         except:
  59.             pass
  60.  
  61.  
  62. class Main:
  63.     # base path
  64.     BASE_CACHE_PATH = os.path.join( xbmc.translatePath( "special://profile/" ), "Thumbnails", "Pictures" )
  65.     INSTALLED_ITEMS_FILENAME = os.path.join( os.getcwd(), "installed_items.dat" )
  66.  
  67.     def __init__( self ):
  68.         log( "%s init!" % self.__class__ )
  69.         ok = False
  70.         # parse sys.argv for our current url
  71.         self._parse_argv()
  72.         # if this is first run list all the repos
  73.         if ( sys.argv[ 2 ] == "" ):
  74.             ok = self._get_repos()
  75.         else:
  76.             # get the repository info
  77.             repo_info = get_repo_info( self.args.repo )
  78.             if repo_info:
  79.                 self.REPO_URL, self.REPO_ROOT, self.REPO_STRUCTURES = repo_info
  80.                 # if category is root, set our repo root
  81.                 if ( self.args.category == "root" ):
  82.                     self.args.category = self.REPO_ROOT
  83.  
  84.                 # get XBMC revision
  85.                 self.XBMC_REVISION = get_xbmc_revision()
  86.                 # get the list
  87.                 ok = self._show_categories()
  88.         # send notification we're finished, successfully or unsuccessfully
  89.         xbmcplugin.endOfDirectory( handle=int( sys.argv[ 1 ] ), succeeded=ok )
  90.  
  91.     def _clear_log( self, repo ):
  92.         base_path = os.path.join( xbmc.translatePath( "special://profile/" ), "plugin_data", "programs", os.path.basename( os.getcwd() ) )
  93.         for page in range( 3 ):
  94.             path = os.path.join( base_path, "%s%d.txt" % ( repo, page, ) )
  95.             # remove log file
  96.             if ( os.path.isfile( path ) ):
  97.                 os.remove( path )
  98.  
  99.     def _parse_argv( self ):
  100.         # if first run set title to blank
  101.         if ( sys.argv[ 2 ] == "" ):
  102.             self.args = Info( title="" )
  103.         else:
  104.             # call _Info() with our formatted argv to create the self.args object
  105.             exec "self.args = Info(%s)" % ( urllib.unquote_plus( sys.argv[ 2 ][ 1 : ].replace( "&", ", " ) ), )
  106.  
  107.     def _get_repos( self ):
  108.         try:
  109.             # we fetch the log here only at start of plugin
  110.             #import xbmcplugin_logviewer
  111.             # add the check for updates item to the media list
  112.             url = "%s?category='updates'" % ( sys.argv[ 0 ], )
  113.             # set the default icon
  114.             icon = "DefaultFolder.png"
  115.             # set thumbnail
  116.             thumbnail = os.path.join( os.getcwd(), "resources", "media", "update_checker.png" )
  117.             # create our listitem, fixing title
  118.             listitem = xbmcgui.ListItem( xbmc.getLocalizedString( 30500 ), iconImage=icon, thumbnailImage=thumbnail )
  119.             # set the title
  120.             listitem.setInfo( type="Video", infoLabels={ "Title": xbmc.getLocalizedString( 30500 ) } )
  121.             cm = [ ( xbmc.getLocalizedString( 30610 ), "XBMC.RunPlugin(%s?showreadme=True&repo=None&readme=None)" % ( sys.argv[ 0 ], ), ) ]
  122.             listitem.addContextMenuItems( cm, replaceItems=True )
  123.             # add our item
  124.             ok = xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=url, listitem=listitem, isFolder=True )
  125.  
  126.             # now add all the repos
  127.             repos = load_repos()
  128.             for repo in repos:
  129.                 cm = []
  130.                 # create the url
  131.                 url = "%s?category='root'&repo=%s&title=%s" % ( sys.argv[ 0 ], repr( urllib.quote_plus( repo ) ), repr( urllib.quote_plus( repo ) ), )
  132.                 # set thumbnail
  133.                 thumbnail = os.path.join( os.getcwd(), "resources", "media", "svn_repo.png" )
  134.                 # create our listitem, fixing title
  135.                 listitem = xbmcgui.ListItem( repo, iconImage=icon, thumbnailImage=thumbnail )
  136.                 # set the title
  137.                 listitem.setInfo( type="Video", infoLabels={ "Title": repo } )
  138.                 # grab the log for this repo
  139.                 if ( "(tagged)" not in repo ):
  140.                     #parser = xbmcplugin_logviewer.ChangelogParser( repo, parse=False )
  141.                     #parser.fetch_changelog()
  142.                     # clear logs on first run
  143.                     self._clear_log( repo )
  144.                     # add view log context menu item
  145.                     cm += [ ( xbmc.getLocalizedString( 30600 ), "XBMC.RunPlugin(%s?showlog=True&repo=%s&category=None&revision=None&parse=True)" % ( sys.argv[ 0 ], urllib.quote_plus( repr( repo ) ), ), ) ]
  146.                 # add view readme context menu item
  147.                 cm += [ ( xbmc.getLocalizedString( 30610 ), "XBMC.RunPlugin(%s?showreadme=True&repo=None&readme=None)" % ( sys.argv[ 0 ], ), ) ]
  148.                 # add context menu items
  149.                 listitem.addContextMenuItems( cm, replaceItems=True )
  150.                 # add the item to the media list
  151.                 ok = xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=url, listitem=listitem, isFolder=True )
  152.                 # if user cancels, call raise to exit loop
  153.                 if ( not ok ): raise
  154.         except:
  155.             # user cancelled dialog or an error occurred
  156.             logError()
  157.             ok = False
  158.         if ( ok ):
  159.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_LABEL )
  160.         return ok
  161.  
  162.     def _show_categories( self ):
  163.         ok = False
  164.         # fetch the html source
  165.         items = self._get_items()
  166.         # if successful
  167.         if ( items and items[ "status" ] == "ok" ):
  168.             # if there are assets, we have categories
  169.             ok = self._fill_list( items[ "url" ], items[ "revision" ], items[ "assets" ] )
  170.         return ok
  171.  
  172.     def _fill_list( self, repo_url, revision, assets ):
  173.         log("_fill_list()")
  174.         try:
  175.             ok = False
  176.             # enumerate through the list of categories and add the item to the media list
  177.             all_addons = []
  178.             for item in assets:
  179.                 info={}
  180.                 cm = []
  181.                 isFolder = True
  182.                 for name, noffset, install, ioffset, voffset in self.REPO_STRUCTURES:
  183.                     try:
  184.                         if ( repo_url.split( "/" )[ int( noffset ) ].lower() == name.lower() ):
  185.                             isFolder = False
  186.                             break
  187.                     except:
  188.                         pass
  189.  
  190.                 if ( isFolder ):
  191.                     heading = "category"
  192.                     thumbnail = ""
  193.                     label2 = ""
  194.                     version = ""
  195.                     path = ""
  196.                 else:
  197.                     heading = "download_url"
  198.                     thumbnail = "%s%s/%sdefault.tbn" % ( self.REPO_URL, repo_url.replace( " ", "%20" ), item.replace( " ", "%20" ), )
  199.                     info['thumb'] = thumbnail
  200.                     svn_url = "%s%s/%sdefault.py" % ( self.REPO_URL, repo_url.replace( " ", "%20" ), item.replace( " ", "%20" ), )
  201.                     svn_tagInfo, installed_version, label2, path = self._check_compatible( svn_url, self.REPO_URL, install, int( ioffset ), int( voffset ) )
  202.                     info.update(svn_tagInfo)
  203.                     info['svn_url'] = svn_url.replace('/default.py','')
  204.                     info['svn_ver'] = svn_tagInfo['version']
  205.                     info['version'] = installed_version
  206.                     info['filepath'] = path.replace(os.sep + 'default.py','')
  207.                     if not info.get('title',''):
  208.                         info['title'] = urllib.unquote_plus( item[ : -1 ] )
  209.                     info['install'] = install
  210.                     info['ioffset'] = ioffset
  211.                     info['voffset'] = voffset
  212.                     info['repo'] = self.args.repo
  213.                     info['category'] = parseCategory(urllib.unquote_plus(info['svn_url']))
  214.                     version = info['svn_ver']
  215.                     if not version:
  216.                         version = "?"
  217.                     version = " (v%s)" % version
  218.  
  219.                     # discover if a readme exists, save url
  220.                     readme_url_base = "%s%s/%s" % ( self.REPO_URL, repo_url, item, )
  221.                     readme = check_readme( readme_url_base )
  222.                     info['readme'] = readme
  223.  
  224.                 if heading == "category":
  225.                     url = '%s?%s="%s/%s"&repo=%s&install="%s"&ioffset=%s&voffset=%s&title=%s' % \
  226.                         ( sys.argv[ 0 ], heading, urllib.quote_plus( repo_url ), urllib.quote_plus( item ), repr( urllib.quote_plus( self.args.repo ) ), \
  227.                         install, ioffset, voffset, repr( urllib.quote_plus( self.args.repo ) ), )
  228.                 elif heading == "download_url":
  229.                     # dont add install url to info dict if incompatible
  230.                     if xbmc.getLocalizedString( 30015 ) not in label2:
  231.                         dl = "%s/%s" % ( urllib.quote_plus( repo_url ), urllib.quote_plus( item ) )
  232.                         info['download_url'] = "download_url=%s&repo=%s&install=%s&ioffset=%s&voffset=%s" % \
  233.                             ( repr( dl ), repr( urllib.quote_plus( self.args.repo ) ), repr(install), ioffset, voffset )
  234.  
  235.                     url_args = "show_info=%s" % urllib.quote_plus( repr(info['filepath']) )
  236.                     url = '%s?%s' % ( sys.argv[ 0 ], url_args, )
  237.                     
  238.                 # add uninstall item
  239.                 if ( not isFolder and not "SVN%20Repo%20Installer" in item and os.path.isfile( path ) ):
  240.                     cm +=  [ ( xbmc.getLocalizedString( 30022 ), "XBMC.RunPlugin(%s?delete=%s&title=%s&delete_from_list=True)" % ( sys.argv[ 0 ], urllib.quote_plus( repr( os.path.dirname( path ) ) ), repr( urllib.quote_plus( item[ : -1 ] ) ), ), ) ]
  241.                     
  242.                 # set the default icon
  243.                 if isFolder:
  244.                     icon = "DefaultFolder.png"
  245.                 else:
  246.                     icon = "DefaultFile.png"
  247.                 # create our listitem, fixing title
  248.                 label1 = "%s%s" % ( urllib.unquote_plus( item[ : -1 ] ), version, )
  249.                 listitem = xbmcgui.ListItem( label1, label2=label2, iconImage=icon, thumbnailImage=thumbnail )
  250.                 # set the title
  251.                 listitem.setInfo( type="Video", infoLabels={ "Title": label1, "Genre": label2 } )
  252.                 if ( not isFolder ):
  253.                     cm += [ ( xbmc.getLocalizedString( 30600 ), "XBMC.RunPlugin(%s?showlog=True&repo=%s&category=%s&revision=None&parse=True)" % ( sys.argv[ 0 ], urllib.quote_plus( repr( self.args.repo ) ), urllib.quote_plus( repr( item[ : -1 ].replace( "%20", " " )  )  ), ), ) ]
  254.                     # add context menu items
  255.                     if ( readme ):
  256.                         cm += [ ( xbmc.getLocalizedString( 30610 ), "XBMC.RunPlugin(%s?showreadme=True&repo=None&readme=%s)" % ( sys.argv[ 0 ], urllib.quote_plus( repr( readme ) ), ), ) ]
  257.                 listitem.addContextMenuItems( cm, replaceItems=True )
  258.  
  259.                 all_addons.append(info)
  260.  
  261.                 # add the item to the media list
  262.                 ok = xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=url, listitem=listitem, isFolder=isFolder, totalItems=len( assets ) )
  263.                 # if user cancels, call raise to exit loop
  264.                 if ( not ok ): raise
  265.  
  266.             # save info to file
  267. #            pprint (all_addons)
  268.             saveFileObj(self.INSTALLED_ITEMS_FILENAME, all_addons)
  269.         except:
  270.             # user cancelled dialog or an error occurred
  271.             logError()
  272.             ok = False
  273.         if ( ok ):
  274.             # set our plugin category
  275.             xbmcplugin.setPluginCategory( handle=int( sys.argv[ 1 ] ), category=self.args.title )
  276.             # sort by genre so all update status' are grouped
  277.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_GENRE )
  278.  
  279.         return ok
  280.  
  281.     def _check_compatible( self, url, repo_url, install, ioffset, voffset ):
  282.         log("> _check_compatible() url=%s" % (url, ) )
  283.  
  284.         # get items svn info
  285.         htmlSource = readURL( url )
  286.  
  287.         # parse source for revision and version
  288.         svn_tagInfo = parseAllDocTags( htmlSource )
  289.         svn_version = svn_tagInfo.get('version','')
  290.         svn_xbmc_rev = svn_tagInfo.get('XBMC_Revision',0)
  291.         # compatible - 0 == unknown, so allow it
  292.         ok = bool((not self.XBMC_REVISION) or (not svn_xbmc_rev) or
  293.                   (self.XBMC_REVISION >= svn_xbmc_rev))
  294.  
  295.         # create path
  296.         items = url.replace( repo_url, "" ).split( "/" )
  297.         # base path
  298.         drive = xbmc.translatePath( "/".join( [ "special://home", install ] ) )
  299.         if ( voffset != 0 ):
  300.             items[ voffset - 1 ] = "%s - %s" % ( items[ voffset - 1 ].replace( "%20", " " ), items[ voffset ], )
  301.             del items[ voffset ]
  302.         path = os.path.join( drive, os.path.sep.join( items[ ioffset : ] ).replace( "%20", " " ) )
  303.  
  304.         # discover installed version
  305.         version = ""
  306.         isInstalled = os.path.isfile(path)
  307.         if isInstalled:
  308.             htmlSource = open( path, "r" ).read()
  309.             version = parseDocTag( htmlSource, "version" )
  310.  
  311.         if ( not ok ): 
  312.             verState = xbmc.getLocalizedString( 30015 )                            # Incompatible
  313.         elif not isInstalled:
  314.             verState = xbmc.getLocalizedString( 30021 )                            # install
  315.         else:
  316.             # determine if svn update; always NEW if installed ver unknown
  317.             if (not version and svn_version) or (svn_version and svn_version > version):
  318.                 verState = xbmc.getLocalizedString( 30014 )                        # New
  319.             else:
  320.                 verState = xbmc.getLocalizedString( 30011 )                        # OK
  321.  
  322.         # make label2 according to state
  323.         label2 = makeLabel2( verState )
  324.         log("< _check_compatible() installed ver=%s label2=%s path=%s" % (version, label2, path))
  325.         return svn_tagInfo, version, label2, path
  326.  
  327.     def _get_items( self ):
  328.         try:
  329.             # open url
  330.             url = self.REPO_URL + self.args.category
  331.             htmlSource = readURL( url )
  332.             # parse source and return a dictionary
  333.             return Parser( htmlSource ).dict
  334.         except:
  335.             # oops print error message
  336.             print "ERROR: %s::%s (%d) - %s" % ( self.__class__.__name__, sys.exc_info()[ 2 ].tb_frame.f_code.co_name, sys.exc_info()[ 2 ].tb_lineno, sys.exc_info()[ 1 ], )
  337.             return {}
  338.